﻿using System.Collections.Generic;
using System;

namespace AccessControl.Utility.UI
{

    public class SearchFilterCollection : List<FilterPart>
    {

        public enum AddTypeConstants
        {
            AddOr = 0,
            AddAnd = 1
        }

        public bool IsChanged { get; set; }
        public bool NoCode { get; set; }
        public bool Bitwise { get; set; }
        public int MaxBitValue { get; set; }

        public SearchFilterCollection()
        {

        }

        #region overrides

        public new void Add(FilterPart filter)
        {
            IsChanged = true;
            base.Add(filter);
        }

        public new void Remove(FilterPart filter)
        {
            IsChanged = true;
            base.Remove(filter);
        }

        public new void Clear()
        {
            if (this.Count > 0) IsChanged = true;
            base.Clear();
        }

        public new void AddRange(IEnumerable<FilterPart> filters)
        {
            IsChanged = true;
            base.AddRange(filters);
        }
        #endregion

        //public void AddFilter(object Code, GridView view, GridColumn colCode, AddTypeConstants addType) {
        //    int RowIndex = view.GetDataSourceRowIndex(view.LocateByDisplayText(0, colCode, Code.ToString()));
        //    AddFilter2(Code, RowIndex, addType);
        //}

        //public void AddFilter2(object Code, int DBRowIndex, AddTypeConstants addType) {
        //    FilterMatch match = SearchInFilters(Code, DBRowIndex, null, addType);
        //    AddFilter(match, new FilterPart(new DBRow(Code, DBRowIndex)), addType);
        //}

        public void AddFilter(FilterMatch match, FilterPart singleFilter, AddTypeConstants addType)
        {
            if (addType == AddTypeConstants.AddAnd)
            {
                this.Clear();
                if (this.Count == 0 || match.IsIn)
                {
                    this.Add(singleFilter);
                }
            }
            else
            {
                if (match.IsMatch == false || NoCode)
                {
                    if (!this.Contains(singleFilter)) this.Add(singleFilter);
                }
                else if (match.isNearEnd && match.IsFirst)
                {
                    match.NearEnd.endRow = match.IncludingFilter.endRow;
                    this.Remove(match.IncludingFilter);
                    this.Remove(singleFilter);
                }
                else if (match.IsLast && match.isNearStart)
                {
                    match.IncludingFilter.endRow = match.NearStart.endRow;
                    this.Remove(match.NearStart);
                    this.Remove(singleFilter);
                }
                else if (match.isNearEnd && match.isNearStart)
                {
                    match.NearEnd.endRow = match.NearStart.endRow;
                    this.Remove(match.NearStart);
                    this.Remove(singleFilter);
                }
                else if (match.isNearEnd)
                {
                    match.NearEnd.endRow = singleFilter.startRow;
                    this.Remove(singleFilter);
                }
                else if (match.isNearStart)
                {
                    match.NearStart.startRow = singleFilter.startRow;
                    this.Remove(singleFilter);
                }
                else if (match.IsIn)
                {
                    this.Remove(singleFilter);
                }
            }
        }
        public void AddFilter(DBRow startRow, DBRow endRow, AddTypeConstants addType)
        {
            var filter = new FilterPart(startRow, endRow);
            AddFilter(filter, addType);
        }
        public void AddFilter(DBRow singleCode, AddTypeConstants addType)
        {
            var filterPart = new FilterPart(singleCode);
            AddFilter(filterPart, addType);
        }
        public void AddFilter(FilterPart filter, AddTypeConstants addType)
        {
            FilterMatch startMatch = SearchInFilters(filter.startRow.Code, filter.startRow.DBRowIndex, filter, addType);

            if (filter.isSingleItem())
                AddFilter(startMatch, filter, addType);
            else
            {

                FilterMatch endMatch = SearchInFilters(filter.endRow.Code, filter.endRow.DBRowIndex, filter, addType);

                //Remove unneeccary filters
                if (startMatch.IsIn && startMatch.isNearEnd)
                {
                    startMatch.NearEnd.endRow = startMatch.IncludingFilter.endRow;
                    this.Remove(startMatch.IncludingFilter);
                }
                else if (endMatch.IsIn && endMatch.isNearStart)
                {
                    endMatch.IncludingFilter.endRow = endMatch.NearStart.endRow;
                    this.Remove(endMatch.NearStart);
                }
                else if (startMatch.isNearStart && startMatch.isNearEnd)
                {
                    startMatch.NearEnd.endRow = startMatch.NearStart.endRow;
                    this.Remove(startMatch.NearStart);
                }
                else if (endMatch.isNearEnd && endMatch.isNearStart)
                {
                    endMatch.NearEnd.endRow = endMatch.NearStart.endRow;
                    this.Remove(endMatch.NearStart);
                }

                FilterPart startFilter, endFilter;
                if (addType == AddTypeConstants.AddOr)
                {

                    if (!startMatch.IsMatch && !endMatch.IsMatch)
                    {
                        if (!this.Contains(filter)) this.Add(filter);
                    }
                    else if (startMatch.InOrNearEnd && endMatch.InOrNearFirst && (startFilter = (startMatch.NearEnd ?? startMatch.IncludingFilter)) != (endFilter = (endMatch.IncludingFilter ?? endMatch.NearStart)))
                    {
                        startFilter.endRow = endFilter.endRow;
                        this.Remove(endFilter);
                        this.Remove(filter);
                    }
                    else if (startMatch.IsMatch && !endMatch.IsMatch)
                    {
                        startFilter = startMatch.IncludingFilter ?? startMatch.NearStart ?? startMatch.NearEnd;
                        if (startMatch.isNearStart) startFilter.startRow = filter.startRow;
                        startFilter.endRow = filter.endRow;
                        this.Remove(filter);
                    }
                    else if (!startMatch.IsMatch && endMatch.IsMatch)
                    {
                        endFilter = endMatch.NearEnd ?? endMatch.IncludingFilter ?? endMatch.NearStart;
                        if (endMatch.isNearEnd) endFilter.endRow = filter.endRow;
                        endFilter.startRow = filter.startRow;
                        this.Remove(filter);
                    }
                    else if (startMatch.IsIn && endMatch.IsIn && startMatch.IncludingFilter == endMatch.IncludingFilter)
                    {
                        this.Remove(filter);
                    }


                }
                else
                {
                    this.Remove(filter);
                    if (this.Count == 0)
                        this.Add(filter);
                    else if (startMatch.IsIn && endMatch.IsIn && startMatch.IncludingFilter == endMatch.IncludingFilter)
                    {
                        startMatch.IncludingFilter.startRow = filter.startRow;
                        startMatch.IncludingFilter.endRow = filter.endRow;
                    }
                    else if (startMatch.IsIn && endMatch.IsIn && startMatch.IncludingFilter != endMatch.IncludingFilter)
                    {
                        startMatch.IncludingFilter.startRow = filter.startRow;
                        endMatch.IncludingFilter.endRow = filter.endRow;
                    }
                    else if (startMatch.IsIn && !endMatch.IsIn)
                    {
                        startMatch.IncludingFilter.startRow = filter.startRow;
                    }
                    else if (!startMatch.IsMatch && endMatch.IsMatch)
                    {
                        endMatch.IncludingFilter.endRow = filter.endRow;
                    }

                    for (int item = this.Count - 1; item >= 0; item--)
                        if (!(this[item].startRow.DBRowIndex >= filter.startRow.DBRowIndex && this[item].endRow.DBRowIndex <= filter.endRow.DBRowIndex))
                            this.Remove(this[item]);

                    //removeExtraFilters(filter.startRow, filter.endRow);
                }
            }
        }

        //public void AddFilter2(object startCode, object endCode, GridView view, GridColumn colCode, AddTypeConstants addType) {
        //    int StartIndex = view.GetDataSourceRowIndex(view.LocateByDisplayText(0, colCode, startCode.ToString()));
        //    int EndIndex = view.GetDataSourceRowIndex(view.LocateByDisplayText(0, colCode, startCode.ToString()));
        //    AddFilter2(new DBRow(startCode, StartIndex), new DBRow(endCode, EndIndex), view, colCode, addType);

        //}

        public void RemoveFilter(DBRow row, object prevCode, object nextCode)
        {
            RemoveFilter(row.Code, row.DBRowIndex, prevCode, nextCode);
        }

        private void RemoveFilter(object Code, int DBRowIndex, object prevCode, object nextCode)
        {
            FilterMatch match = SearchInFilters(Code, DBRowIndex, null, null);
            if (match.IsMatch)
            {
                if (match.IncludingFilter.isSingleItem())
                {
                    if (match.IsIn)
                        this.Remove(match.IncludingFilter);
                }
                else
                {
                    if (match.IsFirst)
                        match.IncludingFilter.startRow = new DBRow(nextCode, DBRowIndex + 1);
                    else if (match.IsLast)
                        match.IncludingFilter.endRow = new DBRow(prevCode, DBRowIndex - 1);
                    else if (match.IsInMiddle)
                    {
                        var part2 = new FilterPart(new DBRow(nextCode, DBRowIndex + 1), match.IncludingFilter.endRow);
                        match.IncludingFilter.endRow = new DBRow(prevCode, DBRowIndex - 1);
                        this.Add(part2);
                    }
                }
            }

        }

        public SearchFilterCollection Clone()
        {
            var fc = new SearchFilterCollection();
            foreach (FilterPart filter in this)
                fc.Add(filter.Clone());
            fc.NoCode = NoCode;
            fc.IsChanged = IsChanged;
            return fc;
        }

        private FilterMatch SearchInFilters(object Code, int dbRowIndex, FilterPart selffilter, AddTypeConstants? addType)
        {
            FilterMatch result = new FilterMatch();
            foreach (FilterPart filter in this)
            {
                if (selffilter != filter)
                {
                    result.IsInMiddle = dbRowIndex > filter.startRow.DBRowIndex && dbRowIndex < filter.endRow.DBRowIndex;
                    result.IsFirst = dbRowIndex == filter.startRow.DBRowIndex;
                    result.IsLast = dbRowIndex == filter.endRow.DBRowIndex;
                    if (addType.HasValue && addType.Value == AddTypeConstants.AddOr)
                    {
                        if (dbRowIndex == filter.startRow.DBRowIndex - 1)
                        {
                            result.NearStart = filter;
                            result.isNearStart = true;
                        }
                        if (dbRowIndex == filter.endRow.DBRowIndex + 1)
                        {
                            result.NearEnd = filter;
                            result.isNearEnd = true;
                        }
                    }

                    if (result.IsInMiddle || result.IsFirst || result.IsLast)
                    {
                        result.IncludingFilter = filter;
                        break;
                    }
                }
            }
            return result;
        }

        private void removeExtraFilters(DBRow startCode, DBRow endCode)
        {
            for (int item = this.Count - 1; item >= 0; item--)
            {
                if (this.Count > item && !(this[item].startRow.DBRowIndex >= startCode.DBRowIndex && this[item].endRow.DBRowIndex <= endCode.DBRowIndex))
                    this.RemoveAt(item);
            }
        }

        public void removeExtraFilters()
        {
            for (int item = this.Count - 1; item >= 0; item--)
            {
                if (this.Count > item && this[item] != null)
                    AddFilter(this[item], AddTypeConstants.AddOr);
            }
        }


        public override string ToString()
        {
            string value = string.Empty;
            foreach (FilterPart filter in this)
            {
                if (NoCode)
                    value += "[" + filter.startRow.Description + "] ";
                else if (filter.isSingleItem())
                    value += filter.startRow.Code + ";";
                else
                    value += filter.startRow.Code + "-" + filter.endRow.Code + ";";
            }
            return value;
        }

        public string getFilterString(string SearchMember)
        {
            if (this.Count == 0)
                return "";

            var rangeFilters = new List<string>();
            var singleFilters = new List<string>();
            foreach (FilterPart filter in this)
            {
                if (filter.isSingleItem())
                    singleFilters.Add(filter.getFilterString(SearchMember));
                else
                    rangeFilters.Add(filter.getFilterString(SearchMember));
            }
            var filterString = new List<string>();
            if (rangeFilters.Count > 0)
                filterString.Add(string.Join(" OR ", rangeFilters.ToArray()));
            if (singleFilters.Count > 0)
            {
                if (Bitwise)
                {
                    int TotalFromStart = 0, Total = 0;
                    //for(int f1=0;f1<this.Count;f1++) {
                    //    for(int f2=f1+1;f2<this.Count;f2++)
                    //        if((int)this[f1].startRow.Code!=(int)this[f2].startRow.Code){
                    //            Total=(int)this[f1].startRow.Code+(int)this[f2].startRow.Code;
                    //            TotalFromStart += Total;
                    //            singleFilters.Add(Total.ToString());
                    //            if(TotalFromStart!=Total) singleFilters.Add(TotalFromStart.ToString());
                    //        }
                    //}
                    for (int f1 = 0; f1 < this.Count; f1++)
                    {
                        for (int f2 = 1; f2 < MaxBitValue; f2++)
                            if (((int)this[f1].startRow.Code & f2) > 0)
                            {
                                if (!singleFilters.Contains(f2.ToString())) singleFilters.Add(f2.ToString());
                                //if (TotalFromStart != Total) singleFilters.Add(TotalFromStart.ToString());
                            }
                    }
                }
                filterString.Add(SearchMember + " IN (" + string.Join(",", singleFilters.ToArray()) + ")");
            }

            return "((" + string.Join(") OR (", filterString.ToArray()) + "))";
        }

        public string getFilterString()
        {
            if (this.Count == 0)
                return "";

            var rangeFilters = new List<string>();
            var singleFilters = new List<string>();
            foreach (FilterPart filter in this)
            {
                if (filter.isSingleItem())
                    singleFilters.Add(filter.getFilterString(String.Empty));
                else
                    rangeFilters.Add(filter.getFilterString(String.Empty));
            }
            var filterString = new List<string>();
            if (rangeFilters.Count > 0)
                filterString.Add(string.Join(" OR ", rangeFilters.ToArray()));
            if (singleFilters.Count > 0)
            {
                if (Bitwise)
                {
                    int TotalFromStart = 0, Total = 0;
                    //for(int f1=0;f1<this.Count;f1++) {
                    //    for(int f2=f1+1;f2<this.Count;f2++)
                    //        if((int)this[f1].startRow.Code!=(int)this[f2].startRow.Code){
                    //            Total=(int)this[f1].startRow.Code+(int)this[f2].startRow.Code;
                    //            TotalFromStart += Total;
                    //            singleFilters.Add(Total.ToString());
                    //            if(TotalFromStart!=Total) singleFilters.Add(TotalFromStart.ToString());
                    //        }
                    //}
                    for (int f1 = 0; f1 < this.Count; f1++)
                    {
                        for (int f2 = 1; f2 < MaxBitValue; f2++)
                            if (((int)this[f1].startRow.Code & f2) > 0)
                            {
                                if (!singleFilters.Contains(f2.ToString())) singleFilters.Add(f2.ToString());
                                //if (TotalFromStart != Total) singleFilters.Add(TotalFromStart.ToString());
                            }
                    }
                }
                filterString.Add("(" + string.Join(",", singleFilters.ToArray()) + ")");
            }

            return string.Join(") OR (", filterString.ToArray()) ;
        }

    }


    public enum PointLocations
    {
        IsFirst = 1,
        IsLast = 2,
        IsMiddle = 4,
        IsNearStart = 8,
        IsNearEnd = 16
    }
    public struct FilterMatch
    {
        public bool IsInMiddle { get; set; }
        public bool IsFirst { get; set; }
        public bool IsLast { get; set; }
        public bool isNearStart { get; set; }
        public bool isNearEnd { get; set; }
        public FilterPart IncludingFilter { get; set; }
        public FilterPart NearStart { get; set; }
        public FilterPart NearEnd { get; set; }
        public bool IsNear
        {
            get { return isNearEnd || isNearStart; }
        }
        public bool IsIn
        {
            get { return IsFirst || IsLast || IsInMiddle; }
        }
        public bool IsMatch
        {
            get { return IsIn || IsNear; }
        }
        public bool InOrNearFirst
        {
            get { return isNearStart || IsFirst || IsInMiddle; }
        }
        public bool InOrNearEnd
        {
            get { return isNearEnd || IsLast || IsInMiddle; }
        }
    }

    public class ReportSearchParams
    {
        public enum ValueTypeConstants
        {
            All = 0,
            Debitors = 1,
            Creditors = 2
        }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public SearchFilterCollection Journals { get; set; }
        public SearchFilterCollection Subsidiaries { get; set; }
        public SearchFilterCollection CostAreas { get; set; }
        public SearchFilterCollection CostCenters { get; set; }
        public SearchFilterCollection SubSbs { get; set; }
        public SearchFilterCollection SubSbs2 { get; set; }
        public SearchFilterCollection References { get; set; }
        public SearchFilterCollection JournalStatuses { get; set; }
        public SearchFilterCollection JournalKinds { get; set; }
        public ValueTypeConstants ValueType { get; set; }
        public bool ShowRemain { get; set; }
    }


    public struct DBRow
    {
        public object Code;
        public int DBRowIndex;
        public string Description;
        public DBRow(object code, int rowIndex)
        {
            Code = code;
            DBRowIndex = rowIndex;
            Description = string.Empty;
        }
        public DBRow(object code, int rowIndex, string description)
            : this(code, rowIndex)
        {
            Description = description;
        }
    }

    public class FilterPart
    {
        public DBRow startRow;
        public DBRow endRow;
        public string getFilterString(string MemberName)
        {
            if (!isSingleItem())
            {
                if (typeof(System.String) == startRow.Code.GetType())
                    return string.Format("{0}>='" + startRow.Code + "' AND {0} <= '" + endRow.Code + "'", MemberName);
                else
                    return string.Format("{0}>=" + startRow.Code.ToString() + " AND {0} <= " + endRow.Code.ToString(), MemberName);
            }
            else if (typeof(System.String) == startRow.Code.GetType())
            {
                return "'" + startRow.Code + "'";
            }
            else return startRow.Code.ToString();
        }

        

        public FilterPart(DBRow start, DBRow end)
        {
            startRow = start;
            endRow = end;
        }
        public FilterPart(DBRow row)
        {
            startRow = row;
            endRow = row;
        }
        public bool isSingleItem()
        {
            return startRow.Code.ToString() == endRow.Code.ToString();
        }
        public FilterPart Clone()
        {
            return new FilterPart(startRow, endRow);
        }
    }
}